package com.jidolstar.astro.datetimes
{
	
	/**
	 * JulianDate, Date, Time를 한번에 관리하기 위해 씀 
	 */ 
	public class AstroDateTime
	{
		protected var _julianDate:JulianDate;
		protected var _date:AstroDate;
		protected var _time:AstroTime;

		/**
		 * 생성자 
		 * @param value 숫자형태 Julian Date관련 값, dateFormat에 따라서 값의 종류가 달라진다. 
		 * @param dateFormat JulianDateFormat.JD, JulianDateFormat.MJD, JulianDateFormat.CJD, JulianDateFormat.RJD 값중 하나가 된다.
		 */ 		
		public function AstroDateTime( value:Number, dateFormat:String = "JD" )
		{
			_julianDate = new JulianDate( value, dateFormat );
			_date = new AstroDate( julianDate );
			_time = new AstroTime( julianDate.hours );			
		}
		
		/**
		 * JulianDate의 설정
		 */ 
		public function get julianDate():JulianDate
		{
			return _julianDate.clone();
		}
		
		public function set julianDate( julianDate:JulianDate ):void
		{
			if( julianDate == null ) return;
			if( _julianDate )
			{
				if( julianDate.equal( _julianDate ) ) return;
			}
			julianDate = julianDate.clone();
			_date = new AstroDate( julianDate );
			_time = new AstroTime( julianDate.hours );
			_julianDate = julianDate;
		}
		
		/**
		 * AstroDate와 AstroTime으로 설정 
		 * @param date 날짜(Greenwich 기반) 
		 * @param time 시간 
		 * @param calendarType 
		 * 		달력 타입. 기본값은 null이다. 
		 * 		특별한 경우에 CalendarKind.GREGORIAN 또는 CalendarKind.JULIAN 를 설정한다.
		 * 		<ul>
		 * 		
		 * 			<li> null값을 설정하는 경우 자동으로 1582.10.04 이전은 Julian Calendar를 적용해 계산하고 1582.10.15 이후에는 Gregorian Calandar를 적용해 계산한다. 
		 * 		 		만약 1582.10.05부터  1582.10.14내의 날짜로 맞추면 ArgumentError가 발생한다.
		 * 			<li> CalendarKind.GREGORIAN로 설정하는 경우 날짜는 Gregorian Calendar로 간주하고 Julian Date를 계산한다. 
		 * 	 		<li> CalendarKind.JULIAN로 설정하는 경우Julian Calendar로 간주하고 Julian Date를 계산한다.
		 * 		</ul>
		 * @return Julian Date 값을 반환한다. 
 		 */ 		
		public function setDateTime( date:AstroDate, time:AstroTime, calendarType:String = null ):void
		{
			if( date == null ) return;
			if( time == null ) time = new AstroTime( 0 ); 
			_date = date;
			_time = time;
			_julianDate = new JulianDate( JulianDate.YMDHMS2jd( date.year, date.month, date.day, time.hour, time.minute, time.seconds, calendarType ) );
		}
		
		/**
		 * 년월일,시분초값으로 설정 
		 * @param year
		 * @param month
		 * @param day
		 * @param hour
		 * @param minute
		 * @param seconds
		 * @param calendarType 
		 * 		달력 타입. 기본값은 null이다. 
		 * 		특별한 경우에 CalendarKind.GREGORIAN 또는 CalendarKind.JULIAN 를 설정한다.
		 * 		<ul>
		 * 		
		 * 			<li> null값을 설정하는 경우 자동으로 1582.10.04 이전은 Julian Calendar를 적용해 계산하고 1582.10.15 이후에는 Gregorian Calandar를 적용해 계산한다. 
		 * 		 		만약 1582.10.05부터  1582.10.14내의 날짜로 맞추면 ArgumentError가 발생한다.
		 * 			<li> CalendarKind.GREGORIAN로 설정하는 경우 날짜는 Gregorian Calendar로 간주하고 Julian Date를 계산한다. 
		 * 	 		<li> CalendarKind.JULIAN로 설정하는 경우Julian Calendar로 간주하고 Julian Date를 계산한다.
		 * 		</ul>
		 * @return Julian Date 값을 반환한다. 
		 */
		public function setYMDHMS( year:int, month:int, day:int, hour:int = 0, minute:int = 0, seconds:Number = 0, calendarType:String = null ):void
		{
			_date = new AstroDate( year, month, day );
			_time = AstroTime.HMS2Time( hour, minute, seconds );
			_julianDate = new JulianDate( JulianDate.YMDHMS2jd( year, month, day, hour, minute, seconds, calendarType ) );
		}
		
		/**
		 * Julian Date 값 반환 
		 */ 
		public function get jd():Number
		{
			return _julianDate.jd;
		}
		
		/**
		 * Julian Date의 자정 시작값 
		 * <p>
		 * astroDateTime.date.jd 처럼 사용하지 말자. 왜냐하면 date.clone()를 호출하므로 퍼포먼스가 떨어진다.
		 * </p>
		 */ 
		public function get jd0():Number
		{
			return _date.jd;
		}
		
		/**
		 * Modified Julian Date(MDJ)
		 * <p>MJD는 정오가 아닌 자정에 시작하며 MDJ = JD - 2400000.5 로 계산되어 진다.</p> 
		 */		
		public function get mjd():Number
		{
			return _julianDate.mjd;
		}

		/**
		 * Julian day number (Julian Date의 시작은 정오가 기준이다.)
		 */ 		
		public function get jdn():Number
		{
			return _julianDate.jdn;
		}
		
		/**
		 * Reduced Julian Date
		 * <p>천문학자들에 의해 사용되며 1858-11-16 12:00(화요일)을 기준으로 하는 적일이다. 
		 * Julian Date(JD)에서 2,400,000을 뺀 값이다.
		 * </p>
		 */ 	
		public function get rjd():Number
		{
			return _julianDate.rjd;
		}
		
		/**
		 * AstroDate의 인스턴스
		 * <p>
		 * astroDateTime.date.jd 처럼 사용하지 말자. 왜냐하면 date.clone()를 호출하므로 퍼포먼스가 떨어진다.
		 * 대신 astroDateTime.jd0 를 사용하면 되겠다.
		 * </p>
		 */ 
		public function get date():AstroDate
		{
			return _date.clone();
		}
		
		/**
		 * AstroTime의 인스턴스 
		 */ 
		public function get time():AstroTime
		{
			return _time.clone();
		}
		
		/**
		 * 년도 
		 */ 
		public function get year():int
		{
			return _date.year;
		}
		
		/**
		 * 월(1~12)
		 */ 
		public function get month():int
		{
			return _date.month;
		}
		
		/**
		 * 일(1~31)
		 */ 
		public function get day():int
		{
			return _date.day;
		}
		
		/**
		 * 시/분/초를 시간으로 바꾼값 
		 */ 
		public function get hours():Number
		{
			return _time.hours;
		}

		/**
		 * 시간(0~23)
		 */ 
		public function get hour():int
		{
			return _time.hour;
		}
		
		/**
		 * 분(0~59)
		 */ 		
		public function get minute():int
		{
			return _time.minute;
		}
		
		/**
		 * 초
		 */ 
		public function get seconds():Number
		{
			return _time.seconds;
		}
		
		/**
		 * 해당 년도의 날 수 
		 */ 
		public function get dayNumberOfTheYear():int
		{
			return _date.dayNumberOfTheYear;
		}
		
		/**
		 * 해당 달의 날 수 
		 */ 
		public function get dayNumberOfTheMonth():int
		{
			return _date.dayNumberOfTheMonth;
		}
		
		/**
		 * 해당 주의 요일 
		 * <p>
		 * 0:Sunday, 1:Monday, 2:Thuseday, 3:Wednesday, 4:Thursday, 5:Friday, 6:Saturday
		 * </p>
		 */ 
		public function get dayOfTheWeek():int
		{
			return _date.dayOfTheWeek;
		}
		
		/**
		 * 윤년인가?
		 */ 
		public function get isLeapYear():Boolean
		{
			return _date.isLeapYear;
		}
		
		/**
		 * 클론 생성 
		 */ 
		public function clone():AstroDateTime
		{
			return new AstroDateTime( this.jd, JulianDateFormat.JD );
		}
		
		/**
		 * 같은 값을 가지고 있는지 검사 
		 */ 
		public function equal( dateTime:AstroDateTime ):Boolean
		{
			if( dateTime == null ) return false;
			if( dateTime._julianDate.jd == this._julianDate.jd ) return true;
			return false;
		}
		
		/**
		 * 년월일,시분초 값으로 AstroDateTime의 인스턴스 생성 
		 */ 
		public static function YMDHMS2DateTime( year:int, month:int, day:int, hour:int = 0, minute:int = 0, seconds:Number = 0, calendarType:String = null ):AstroDateTime
		{
			var jd:Number = JulianDate.YMDHMS2jd( year, month, day, hour, minute, seconds, calendarType );
			var dateTime:AstroDateTime = new AstroDateTime( jd, JulianDateFormat.JD );
			return dateTime;
		} 
		
		/**
		 * JulianDate의 객체로 AstroDateTime의 인스턴스 생성 
		 * @param julianDate
		 * @return AstroDateTime객체 
		 */ 
		public static function julianDate2DateTime( julianDate:JulianDate ):AstroDateTime
		{
			var dateTime:AstroDateTime = new AstroDateTime( julianDate.jd, JulianDateFormat.JD );
			return dateTime;
		}
	}
}